#include "multieditbox.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>

#include "editbox.h"
#include "window.h"
#include "text.h"
#include "_window.h"
#include "_multieditbox.h"
#include "multieditbox.h"

#ifdef __DEBUG__
#define __E(fmt, args...) fprintf(stderr, "MULTIEDITBOX ERROR [%d]: " fmt, __LINE__, ## args)
#define __W(fmt, args...) fprintf(stderr, "MULTIEDITBOX WARNING [%d]: " fmt, __LINE__, ## args)
#else
#define __E(fmt, args...)
#define __W(fmt, args...)
#endif

#define false       0
#define true        1

static multieditbox meditbox[MULTIEDITBOX_MAX_NUMBER];
static int multieditboxId = 0;

int multieditbox_create(int qt_lines, unsigned int x, unsigned int y, unsigned int width,
        unsigned int font_height, unsigned int font_r, unsigned int font_g,
        unsigned int font_b, unsigned int r, unsigned int g, unsigned int b) {

    if (multieditboxId > MULTIEDITBOX_MAX_NUMBER) {
        __E("Nao e possivel criar mais multieditbox. Numero maximo.\n");
        return MULTIEDITBOX_FAILURE;
    } else {
        meditbox[multieditboxId].id = multieditboxId;
        meditbox[multieditboxId].actual_line = 0;
        meditbox[multieditboxId].qt_lines = qt_lines;
        meditbox[multieditboxId].lines = malloc(sizeof (int) * qt_lines);

        if (meditbox[multieditboxId].lines == NULL) {
            printf("Falhou o malloc no multieditbox_create.\n");
            return MULTIEDITBOX_FAILURE;
        }

        meditbox[multieditboxId].windowId = -1;
        meditbox[multieditboxId].visible = true;
        meditbox[multieditboxId].initialized = false;
        meditbox[multieditboxId].previous = &meditbox[multieditboxId];
        meditbox[multieditboxId].next = &meditbox[multieditboxId];
    }
    int i;
    for (i = 0; i < qt_lines; i++)
        meditbox[multieditboxId].lines[i] = editbox_create(x, y + i * 20, width, r, b, g, "",
            font_height, font_r, font_b, font_g, 0); /* Multieditboxes tem borda 0 */

    meditbox[multieditboxId].initialized = true;
    return multieditboxId++;
}

signed int multieditbox_destroy(signed int multieditboxId) {
    if (!meditbox[multieditboxId].initialized) {
        __E("Multieditbox nao pode ser destruido: multieditbox nao foi criado.");
        return MULTIEDITBOX_FAILURE;
    }

    meditbox[multieditboxId].actual_line = -1;
    multieditbox_delFromWindow(multieditboxId);

    signed int aux;
    for (aux = meditbox[multieditboxId].lines[0]; aux < meditbox[multieditboxId].qt_lines + meditbox[multieditboxId].lines[0]; aux++) {
        editbox_destroy(meditbox[multieditboxId].lines[aux]);
    }

    meditbox[multieditboxId].visible = 0;
    meditbox[multieditboxId].qt_lines = 0;
    free(meditbox[multieditboxId].lines);
    meditbox[multieditboxId].initialized = 0;
    return MULTIEDITBOX_SUCCESS;
}

int multieditbox_draw(int multieditboxId) {
    if (meditbox[multieditboxId].initialized) {
        if (meditbox[multieditboxId].visible) {
            int i;
            for (i = 0; i < meditbox[multieditboxId].qt_lines; i++)
                editbox_draw(meditbox[multieditboxId].lines[i]);
        }
    } else {
        __E("Multieditbox nao inicializado\n");
        return MULTIEDITBOX_FAILURE;
    }
    return MULTIEDITBOX_SUCCESS;
}

int multieditbox_setVisible(int multieditboxId, unsigned char visible) {
    meditbox[multieditboxId].visible = visible;
    int i;
    for (i = 0; i < meditbox[multieditboxId].qt_lines; i++) {
        if (editbox_setVisible(meditbox[multieditboxId].lines[i], meditbox[multieditboxId].visible) == EDITBOX_FAILURE) {
            __E("Falha no editbox_setVisible\n");
            return MULTIEDITBOX_FAILURE;
        }
    }
    return MULTIEDITBOX_SUCCESS;
}

int multieditbox_getText(int multieditboxId, unsigned char *text) {
    char aux[EDITBOX_MAX_TEXT];
    editbox_getText(meditbox[multieditboxId].lines[0], text);
    int i;
    for (i = 1; i < meditbox[multieditboxId].qt_lines; i++) {
        editbox_getText(meditbox[multieditboxId].lines[i], aux);
        if (strlen(aux) > 0) {
            strncat(text, "\n", 1);
            strcat(text, aux);
        }
    }
    return MULTIEDITBOX_SUCCESS;
}

signed int multieditbox_setText(signed int multieditboxId, unsigned char *text) {
    signed int l = 0;

    // lines conterá as novas linhas de texto do multieditbox.
    unsigned char lines[meditbox[multieditboxId].qt_lines][EDITBOX_MAX_TEXT];

    // Zerar lines...
    memset(lines, '\0', meditbox[multieditboxId].qt_lines * EDITBOX_MAX_TEXT);

    // Quebrar text em linhas para o multieditbox...
    unsigned char msgCpy[EDITBOX_MAX_TEXT * meditbox[multieditboxId].qt_lines];
    strncpy(msgCpy, text, meditbox[multieditboxId].qt_lines * EDITBOX_MAX_TEXT);
    unsigned char aux[EDITBOX_MAX_TEXT * meditbox[multieditboxId].qt_lines];
    unsigned char aux2[EDITBOX_MAX_TEXT * meditbox[multieditboxId].qt_lines];
    strcpy(aux, text);
    unsigned char rest[EDITBOX_MAX_TEXT * meditbox[multieditboxId].qt_lines];
    memset(rest, '\0', meditbox[multieditboxId].qt_lines * EDITBOX_MAX_TEXT);
    signed int msgSize = strlen(msgCpy);
    signed int cut = msgSize;
    signed int divider = 2, fit = 1;
    unsigned char temp = '\0';

    do {
        while (font_getStringWidth(msgCpy, multieditbox_getFontHeight(multieditboxId)) >= editbox_getWidth(meditbox[multieditboxId].lines[0])) {
            /* Ainda nao coube, precisa cortar a mensagem */
            fit = 0;
            strcpy(msgCpy, aux); /* Restaurar mensagem */
            cut = strlen(msgCpy) / divider;
            cut += cut / 2;
            divider *= 2;
            strcpy(rest, &msgCpy[cut]);
            temp = msgCpy[cut];
            msgCpy[cut] = '\0';
        }
        /* Coube */
        signed int s;
        for (s = 0; s < strlen(msgCpy); s++) {
            if (msgCpy[s] == '\n') {
                msgCpy[cut] = temp;
                msgCpy[s] = '\0';
                strcpy(rest, &msgCpy[s + 1]);
                break;
            }
        }

        msgSize = strlen(msgCpy);
        msgCpy[msgSize] = '\0';
        strcpy(lines[l], msgCpy);
        l++;
        strcpy(aux, rest);
        strcpy(msgCpy, rest);
        divider = 2;
        if (fit) {
            break;
        }
        fit = 1;
    } while (msgCpy[0] != '\0' && l < meditbox[multieditboxId].qt_lines);

    // Setar textos de lines nos editboxes...
    for (l = 0; l < meditbox[multieditboxId].qt_lines; l++) {
        signed int setText_return = editbox_setText(meditbox[multieditboxId].lines[l], lines[l]);
        if (setText_return == EDITBOX_TEXT_EXCEED) {
            __E("Texto nao coube no multieditbox\n");
            return MULTIEDITBOX_TEXT_EXCEED;
        } else if (setText_return == EDITBOX_FAILURE) {
            __E("Falha ao setar o texto do editbox\n");
            return MULTIEDITBOX_FAILURE;
        }
    }

    // Não coube todo o text?
    if (msgCpy[0] != '\0') {
        return MULTIEDITBOX_TEXT_EXCEED;
    }

    return MULTIEDITBOX_SUCCESS;
}

/*
int multieditbox_setText(int multieditboxId, unsigned char *text) {
    int i, l, j;
    char lines[meditbox[multieditboxId].qt_lines][EDITBOX_MAX_TEXT];

    memset(lines, '\0', meditbox[multieditboxId].qt_lines * EDITBOX_MAX_TEXT);

    for (i = 0, l = 0, j = 0; i < strlen(text) && l < meditbox[multieditboxId].qt_lines; i++) {
        if (text[i] == '\n') {
            lines[l++][j] = '\0';
            j = 0;
        } else {
            lines[l][j++] = text[i];
        }
    }

    for (l = 0; l < meditbox[multieditboxId].qt_lines; l++) {
        int setText_return = editbox_setText(meditbox[multieditboxId].lines[l], lines[l]);
        if (setText_return == EDITBOX_TEXT_EXCEED) {
            if (l + 1 < meditbox[multieditboxId].qt_lines) {
                char ch[2];
                ch[0] = lines[l][strlen(lines[l]) - 1];
                ch[1] = '\0';
                strcpy(lines[l + 1], ch);
            } else {
                __E("Texto nao coube no multieditbox\n");
                return MULTIEDITBOX_TEXT_EXCEED;
            }
        } else if (setText_return == EDITBOX_FAILURE) {
            __E("Falha ao setar o texto do editbox\n");
            return MULTIEDITBOX_FAILURE;
        }
    }
    return MULTIEDITBOX_SUCCESS;
}
*/

signed int multieditbox_addToWindow(signed int multieditboxId, signed int multieditboxId_next,
        signed int windowId, window *winp) {

    if (meditbox[multieditboxId_next].windowId == -1) {
        meditbox[multieditboxId_next].next = meditbox[multieditboxId].next;
        meditbox[multieditboxId].next = &meditbox[multieditboxId_next];
        meditbox[multieditboxId_next].previous = &meditbox[multieditboxId];
        meditbox[multieditboxId_next].next->previous = &meditbox[multieditboxId_next];
        meditbox[multieditboxId_next].windowId = windowId;
        meditbox[multieditboxId].winp = winp;

        signed int i;
        for (i = 0; i < meditbox[multieditboxId_next].qt_lines; i++) {
            if (window_insertEditbox(windowId, meditbox[multieditboxId_next].lines[i]) == WINDOW_FAILURE) {
                __E("Erro ao inserir multieditbox na window.\n");
                return MULTIEDITBOX_FAILURE;
            }
        }
        return MULTIEDITBOX_SUCCESS;
    } else {
        __E("Ja existe uma tela associada ao multieditbox.\n");
        return MULTIEDITBOX_FAILURE;
    }
}

signed int multieditbox_delFromWindow(signed int multieditboxId) {
    if (!meditbox[multieditboxId].initialized) {
        __E("Multieditbox nao criado.\n");
        return MULTIEDITBOX_FAILURE;
    }

    if (meditbox[multieditboxId].winp == NULL) {
        __E("Não há uma janela associada ao multieditbox.\n");
        return MULTIEDITBOX_FAILURE;
    }

    if (meditbox[multieditboxId].next == &meditbox[multieditboxId]) {
        meditbox[multieditboxId].winp->firstMultieditbox = -1;
        meditbox[multieditboxId].winp->actualMultieditbox = -1;
    } else {
        if (meditbox[multieditboxId].winp->actualMultieditbox == multieditboxId) {
            meditbox[multieditboxId].winp->actualMultieditbox = meditbox[meditbox[multieditboxId].winp->actualMultieditbox].previous->id;
        } else if (meditbox[multieditboxId].winp->firstMultieditbox == multieditboxId) {
            meditbox[multieditboxId].winp->firstMultieditbox = meditbox[meditbox[multieditboxId].winp->firstMultieditbox].next->id;
        }

        meditbox[multieditboxId].previous->next = meditbox[multieditboxId].next;
        meditbox[multieditboxId].next->previous = meditbox[multieditboxId].previous;
    }

    signed int aux;
    for (aux = 0; aux < meditbox[multieditboxId].qt_lines; aux++) {
        window_removeEditbox(meditbox[multieditboxId].windowId, meditbox[multieditboxId].lines[aux]);
    }

    meditbox[multieditboxId].previous = &meditbox[multieditboxId];
    meditbox[multieditboxId].next = &meditbox[multieditboxId];
    meditbox[multieditboxId].winp = NULL;
    meditbox[multieditboxId].windowId = -1;
    window_setComponentsRedraw(meditbox[multieditboxId].windowId);
    return MULTIEDITBOX_SUCCESS;
}

int multieditbox_previous(int multieditboxId) {
    if (meditbox[multieditboxId].initialized) {
        return meditbox[multieditboxId].previous->id;
    } else {
        __E("Multieditbox nao foi crado.\n");
        return MULTIEDITBOX_FAILURE;
    }
}

int multieditbox_next(int multieditboxId) {
    if (meditbox[multieditboxId].initialized) {
        return meditbox[multieditboxId].next->id;
    } else {
        __E("Multieditbox nao foi criado.\n");
        return MULTIEDITBOX_FAILURE;
    }
}

void multieditbox_setLineFontColor(int multieditboxId, int line, 
        unsigned int font_red, unsigned int font_green, unsigned int font_blue) {

    if (meditbox[multieditboxId].initialized) {
        editbox_setFontColor(meditbox[multieditboxId].lines[line], font_red, font_green, font_blue);
    } else return;
}

signed int multieditbox_getX(signed int multieditboxId) {
    if (meditbox[multieditboxId].initialized) {
        return editbox_getX(meditbox[multieditboxId].lines[0]);
    } else {
        __E("Multieditbox não foi criado.\n");
        return MULTIEDITBOX_FAILURE;
    }
}

signed int multieditbox_getY(signed int multieditboxId) {
    if (meditbox[multieditboxId].initialized) {
        return editbox_getY(meditbox[multieditboxId].lines[0]);
    } else {
        __E("Multieditbox não foi criado.\n");
        return MULTIEDITBOX_FAILURE;
    }
}

signed int multieditbox_getWidth(signed int multieditboxId) {
    if (meditbox[multieditboxId].initialized) {
        return editbox_getWidth(meditbox[multieditboxId].lines[0]);
    } else {
        __E("Multieditbox não foi criado.\n");
        return MULTIEDITBOX_FAILURE;
    }
}

signed int multieditbox_getHeight(signed int multieditboxId) {
    if (meditbox[multieditboxId].initialized) {
        return editbox_getHeight(meditbox[multieditboxId].lines[0]) * meditbox[multieditboxId].qt_lines;
    } else {
        __E("Multieditbox não foi criado.\n");
        return MULTIEDITBOX_FAILURE;
    }
}

void multieditbox_setRedraw(signed int multieditboxId) {
    int i;
    for (i = 0; i < meditbox[multieditboxId].qt_lines; i++) {
        editbox_setRedraw(meditbox[multieditboxId].lines[i]);
    }
}

unsigned int multieditbox_getFontHeight(signed int multieditboxId) {
    if (meditbox[multieditboxId].initialized) {
        return editbox_getFontHeight(meditbox[multieditboxId].lines[0]);
    } else {
        __E("Multieditbox não foi criado.\n");
        return MULTIEDITBOX_FAILURE;
    }
}
